package com.dayu.finecomm.decoder;

import java.util.List;

import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.dayu.finecomm.protocol.ProtocolPacket;
import com.dayu.finecomm.protocol.Protocols;
import com.dayu.finecomm.protocol.codec.InvalidStream;
import com.dayu.finecomm.protocol.codec.ProtocolDecoder;
import com.dayu.finecomm.protocol.pojo.TPacket;
import com.dayu.finecomm.utils.Helpers;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;

/**
 * @ClassName: HdTcpDecoder
 * @Description:TODO Tcp报文解析器
 * @author: 戴卫国
 * @date: 2018年1月20日 上午12:36:53
 * @Copyright: 2018 dayu Inc. All rights reserved.
 */
public class HdTcpDecoder extends ByteToMessageDecoder {

	String prefix;
	List<ProtocolDecoder<TPacket>> protocols;
	static Logger LOGGER = LoggerFactory.getLogger(HdTcpDecoder.class);

	public HdTcpDecoder(String prefix, List<ProtocolDecoder<TPacket>> protocols) {

		this.prefix = prefix;
		this.protocols = protocols;
	}

	ByteBuf buffer;

	synchronized ByteBuf alloc(ChannelHandlerContext ctx) {

		return buffer == null ? (buffer = ctx.alloc().buffer()) : buffer;
	}

	synchronized void release(ChannelHandlerContext ctx) {

		if (buffer == null)
			return;

		while (buffer.refCnt() > 0)
			buffer.release();
	}

	@Override
	protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {

		// LOGGER.info("HdTcpDecoder - decode - start");
		ByteBuf in = alloc(ctx);
		in.writeBytes(msg);

		LOGGER.info("RAW:[" + Helpers.bytesToHex(in) + "]");

		boolean force = false;
		boolean valid = false;
		ProtocolDecoder<TPacket> cur = null;

		Pair<ProtocolDecoder<TPacket>, Pair<Integer, TPacket>> pair;

		do {
			try {
				pair = Protocols.decode(protocols, in);
			} catch (InvalidStream e) {
				valid = false;
				force = true;
				break;
			}

			if (pair != null) {

				cur = pair.getLeft();

				int length = pair.getRight().getLeft();
				TPacket s = pair.getRight().getRight();

				valid = true;
				s.prefix = prefix;
				s.in = Helpers.bytesToHex(in);

				LOGGER.info("HdTcpDecoder - decode - part one");
				ctx.fireChannelRead(s);

				ProtocolPacket packet = new ProtocolPacket(in.readSlice(length), true);
				out.add(packet);

				// 【回包】 对于TCP报文，只回复有效报文
				if (cur.needEcho() && !timing(ctx, cur)) {
					byte[] b = cur.echo();
					if (b != null) {
						ctx.writeAndFlush(Unpooled.copiedBuffer(b));
					}
				}
			}

		} while (pair != null);

		if (valid && in.readableBytes() == 0) {
			in.clear();// 清理掉多余的垃圾报文
		}

		if ((force && !valid) || !valid && in.readableBytes() > 16 * 1024) {
			LOGGER.error("断开连接HDTCP,valid:" + valid + ",大小:" + in.readableBytes());
			release(ctx);
			ctx.close();
		}

		// LOGGER.info("HdTcpDecoder - decode - end");
	}

	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {

		release(ctx);
	}

	/**
	 * @Title: timing
	 * @Description: 校时
	 * @param: ctx
	 *             通道
	 * @param: cur
	 *             协议解析
	 * @param: @return
	 */
	private boolean timing(ChannelHandlerContext ctx, ProtocolDecoder<?> cur) {

		boolean needTiming = com.dayu.finecomm.utils.Helpers.needTiming();

		if (needTiming) {
			Object timing = cur.timing();

			if (timing != null && timing instanceof String) {

				ctx.writeAndFlush(Unpooled.copiedBuffer(((String) timing).getBytes()));
			}
		}

		return needTiming;
	}
}
